home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / var / lib / python-support / python2.6 / glchess / chess / pgn.pyc (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2009-04-20  |  15.7 KB  |  551 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.6)
  3.  
  4. '''
  5. Implement a PGN reader/writer.
  6.  
  7. See http://www.chessclub.com/help/PGN-spec
  8. '''
  9. __author__ = 'Robert Ancell <bob27@users.sourceforge.net>'
  10. __license__ = 'GNU General Public License Version 2'
  11. __copyright__ = 'Copyright 2005-2006  Robert Ancell'
  12. import re
  13. RESULT_INCOMPLETE = '*'
  14. RESULT_WHITE_WIN = '1-0'
  15. RESULT_BLACK_WIN = '0-1'
  16. RESULT_DRAW = '1/2-1/2'
  17. results = {
  18.     RESULT_INCOMPLETE: RESULT_INCOMPLETE,
  19.     RESULT_WHITE_WIN: RESULT_WHITE_WIN,
  20.     RESULT_BLACK_WIN: RESULT_BLACK_WIN,
  21.     RESULT_DRAW: RESULT_DRAW }
  22. TAG_EVENT = 'Event'
  23. TAG_SITE = 'Site'
  24. TAG_DATE = 'Date'
  25. TAG_ROUND = 'Round'
  26. TAG_WHITE = 'White'
  27. TAG_BLACK = 'Black'
  28. TAG_RESULT = 'Result'
  29. TAG_TIME = 'Time'
  30. TAG_FEN = 'FEN'
  31. TAG_WHITE_TYPE = 'WhiteType'
  32. TAG_WHITE_ELO = 'WhiteElo'
  33. TAG_BLACK_TYPE = 'BlackType'
  34. TAG_BLACK_ELO = 'BlackElo'
  35. TAG_TIME_CONTROL = 'TimeControl'
  36. TAG_TERMINATION = 'Termination'
  37. PLAYER_HUMAN = 'human'
  38. PLAYER_AI = 'program'
  39. TERMINATE_ABANDONED = 'abandoned'
  40. TERMINATE_ADJUDICATION = 'adjudication'
  41. TERMINATE_DEATH = 'death'
  42. TERMINATE_EMERGENCY = 'emergency'
  43. TERMINATE_NORMAL = 'normal'
  44. TERMINATE_RULES_INFRACTION = 'rules infraction'
  45. TERMINATE_TIME_FORFEIT = 'time forfeit'
  46. TERMINATE_UNTERMINATED = 'unterminated'
  47. TOKEN_LINE_COMMENT = 'Line comment'
  48. TOKEN_COMMENT = 'Comment'
  49. TOKEN_ESCAPED = 'Escaped data'
  50. TOKEN_PERIOD = 'Period'
  51. TOKEN_TAG_START = 'Tag start'
  52. TOKEN_TAG_END = 'Tag end'
  53. TOKEN_STRING = 'String'
  54. TOKEN_SYMBOL = 'Symbol'
  55. TOKEN_RAV_START = 'RAV start'
  56. TOKEN_RAV_END = 'RAV end'
  57. TOKEN_XML = 'XML'
  58. TOKEN_NAG = 'NAG'
  59.  
  60. class Error(Exception):
  61.     '''PGN exception class'''
  62.     pass
  63.  
  64.  
  65. class PGNParser:
  66.     '''
  67.     '''
  68.     STATE_IDLE = 'IDLE'
  69.     STATE_TAG_NAME = 'TAG_NAME'
  70.     STATE_TAG_VALUE = 'TAG_VALUE'
  71.     STATE_TAG_END = 'TAG_END'
  72.     STATE_MOVETEXT = 'MOVETEXT'
  73.     STATE_RAV = 'RAV'
  74.     STATE_XML = 'XML'
  75.     
  76.     def __init__(self, maxGames = -1):
  77.         expressions = [
  78.             '\\%.*',
  79.             ';.*',
  80.             '\\{',
  81.             '".*"',
  82.             '[a-zA-Z0-9\\*\\_\\+\\#\\=\\:\\-\\/]+',
  83.             '\\[',
  84.             '\\]',
  85.             '\\$[0-9]{1,3}',
  86.             '\\(',
  87.             '\\)',
  88.             '\\<.*\\>',
  89.             '[.]+']
  90.         self.regexp = re.compile('|'.join(expressions))
  91.         self.tokens = {
  92.             ';': TOKEN_LINE_COMMENT,
  93.             '{': TOKEN_COMMENT,
  94.             '[': TOKEN_TAG_START,
  95.             ']': TOKEN_TAG_END,
  96.             '"': TOKEN_STRING,
  97.             '.': TOKEN_PERIOD,
  98.             '$': TOKEN_NAG,
  99.             '(': TOKEN_RAV_START,
  100.             ')': TOKEN_RAV_END,
  101.             '<': TOKEN_XML,
  102.             '%': TOKEN_ESCAPED }
  103.         for c in '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*':
  104.             self.tokens[c] = TOKEN_SYMBOL
  105.         
  106.         self.games = []
  107.         self.maxGames = maxGames
  108.         self.comment = None
  109.         self.state = self.STATE_IDLE
  110.         self.game = PGNGame()
  111.         self.tagName = None
  112.         self.tagValue = None
  113.         self.prevTokenIsMoveNumber = False
  114.         self.currentMoveNumber = 0
  115.         self.ravDepth = 0
  116.  
  117.     
  118.     def _parseTokenMovetext(self, tokenType, data):
  119.         '''
  120.         '''
  121.         if tokenType is TOKEN_SYMBOL:
  122.             if self.ravDepth != 0:
  123.                 return None
  124.             if results.has_key(data):
  125.                 self.games.append(self.game)
  126.                 self.game = PGNGame()
  127.                 self.prevTokenIsMoveNumber = False
  128.                 self.currentMoveNumber = 0
  129.                 self.ravDepth = 0
  130.                 self.state = self.STATE_IDLE
  131.             else:
  132.                 
  133.                 try:
  134.                     moveNumber = int(data)
  135.                 except ValueError:
  136.                     self.ravDepth != 0
  137.                     self.ravDepth != 0
  138.                     move = PGNMove()
  139.                     move.number = self.currentMoveNumber
  140.                     move.move = data
  141.                     self.game.addMove(move)
  142.                     self.currentMoveNumber += 1
  143.                 except:
  144.                     self.ravDepth != 0
  145.  
  146.                 self.prevTokenIsMoveNumber = True
  147.                 expected = self.currentMoveNumber / 2 + 1
  148.                 if moveNumber != expected:
  149.                     raise Error('Expected move number %i, got %i' % (expected, moveNumber))
  150.                 moveNumber != expected
  151.         elif tokenType is TOKEN_NAG:
  152.             if self.ravDepth != 0:
  153.                 return None
  154.             move = self.game.getMove(self.currentMoveNumber)
  155.             move.nag = data
  156.         elif tokenType is TOKEN_PERIOD:
  157.             if self.ravDepth != 0:
  158.                 return None
  159.             if self.prevTokenIsMoveNumber is False:
  160.                 raise Error('Unexpected period')
  161.             self.prevTokenIsMoveNumber is False
  162.         elif tokenType is TOKEN_RAV_START:
  163.             self.ravDepth += 1
  164.             return None
  165.         self
  166.         if tokenType is TOKEN_RAV_END:
  167.             self.ravDepth -= 1
  168.             return None
  169.         raise Error('Unknown token %s in movetext' % str(tokenType))
  170.  
  171.     
  172.     def parseToken(self, tokenType, data):
  173.         '''
  174.         '''
  175.         if tokenType is TOKEN_LINE_COMMENT or tokenType is TOKEN_COMMENT:
  176.             if self.currentMoveNumber > 0:
  177.                 move = self.game.getMove(self.currentMoveNumber)
  178.                 move.comment = data[1:-1]
  179.             
  180.             return None
  181.         if self.state is self.STATE_MOVETEXT:
  182.             self._parseTokenMovetext(tokenType, data)
  183.         elif self.state is self.STATE_IDLE:
  184.             if tokenType is TOKEN_TAG_START:
  185.                 self.state = self.STATE_TAG_NAME
  186.                 return None
  187.             if tokenType is TOKEN_SYMBOL:
  188.                 self.whiteMove = None
  189.                 self.prevTokenIsMoveNumber = False
  190.                 self.ravDepth = 0
  191.                 self.state = self.STATE_MOVETEXT
  192.                 self._parseTokenMovetext(tokenType, data)
  193.             elif tokenType is TOKEN_ESCAPED:
  194.                 pass
  195.             else:
  196.                 raise Error('Unexpected token %s' % str(tokenType))
  197.         tokenType is TOKEN_SYMBOL
  198.         if self.state is self.STATE_TAG_NAME:
  199.             if tokenType is TOKEN_SYMBOL:
  200.                 self.tagName = data
  201.                 self.state = self.STATE_TAG_VALUE
  202.             else:
  203.                 raise Error('Got a %s token, expecting a %s token' % (repr(tokenType), repr(TOKEN_SYMBOL)))
  204.         tokenType is TOKEN_SYMBOL
  205.         if self.state is self.STATE_TAG_VALUE:
  206.             if tokenType is TOKEN_STRING:
  207.                 self.tagValue = data[1:-1]
  208.                 self.state = self.STATE_TAG_END
  209.             else:
  210.                 raise Error('Got a %s token, expecting a %s token' % (repr(tokenType), repr(TOKEN_STRING)))
  211.         tokenType is TOKEN_STRING
  212.         if self.state is self.STATE_TAG_END:
  213.             if tokenType is TOKEN_TAG_END:
  214.                 self.game.setTag(self.tagName, self.tagValue)
  215.                 self.state = self.STATE_IDLE
  216.             else:
  217.                 raise Error('Got a %s token, expecting a %s token' % (repr(tokenType), repr(TOKEN_TAG_END)))
  218.         tokenType is TOKEN_TAG_END
  219.  
  220.     
  221.     def parseLine(self, line):
  222.         '''Parse a line from a PGN file.
  223.         
  224.         Return an array of tokens extracted from the line.
  225.         '''
  226.         while len(line) > 0:
  227.             if self.comment is not None:
  228.                 end = line.find('}')
  229.                 if end < 0:
  230.                     self.comment += line
  231.                     return True
  232.                 comment = self.comment + line[:end]
  233.                 self.comment = None
  234.                 self.parseToken(TOKEN_COMMENT, comment)
  235.                 line = line[end + 1:]
  236.                 continue
  237.             
  238.             for match in self.regexp.finditer(line):
  239.                 text = line[match.start():match.end()]
  240.                 if text == '{':
  241.                     line = line[match.end():]
  242.                     self.comment = ''
  243.                     break
  244.                     continue
  245.                 
  246.                 try:
  247.                     tokenType = self.tokens[text[0]]
  248.                 except KeyError:
  249.                     raise Error('Unknown token %s' % repr(text))
  250.  
  251.                 self.parseToken(tokenType, text)
  252.             
  253.             if self.comment is None:
  254.                 return True
  255.             continue
  256.             self.comment is None
  257.  
  258.     
  259.     def complete(self):
  260.         if len(self.game.moves) > 0:
  261.             self.games.append(self.game)
  262.         
  263.  
  264.  
  265.  
  266. class PGNMove:
  267.     '''
  268.     '''
  269.     move = ''
  270.     comment = ''
  271.     nag = ''
  272.  
  273.  
  274. class PGNGame:
  275.     '''
  276.     '''
  277.     _strTags = [
  278.         TAG_EVENT,
  279.         TAG_SITE,
  280.         TAG_DATE,
  281.         TAG_ROUND,
  282.         TAG_WHITE,
  283.         TAG_BLACK,
  284.         TAG_RESULT]
  285.     
  286.     def __init__(self):
  287.         self.tagsByName = { }
  288.         self.setTag(TAG_EVENT, '?')
  289.         self.setTag(TAG_SITE, '?')
  290.         self.setTag(TAG_DATE, '????.??.??')
  291.         self.setTag(TAG_ROUND, '?')
  292.         self.setTag(TAG_WHITE, '?')
  293.         self.setTag(TAG_BLACK, '?')
  294.         self.setTag(TAG_RESULT, '*')
  295.         self.moves = []
  296.  
  297.     
  298.     def getLines(self):
  299.         lines = []
  300.         otherTags = list(set(self.tagsByName).difference(self._strTags))
  301.         for name in self._strTags + otherTags:
  302.             value = self.tagsByName[name]
  303.             lines.append('[' + name + ' ' + self._makePGNString(value) + ']')
  304.         
  305.         lines.append('')
  306.         tokens = []
  307.         moveNumber = 0
  308.         for m in self.moves:
  309.             if moveNumber % 2 == 0:
  310.                 tokens.append('%i.' % (moveNumber / 2 + 1))
  311.             
  312.             moveNumber += 1
  313.             tokens.append(m.move)
  314.             if m.nag != '':
  315.                 tokens.append(m.nag)
  316.             
  317.             if m.comment != '':
  318.                 tokens.append('{' + m.comment + '}')
  319.                 continue
  320.         
  321.         tokens.append(self.tagsByName[TAG_RESULT])
  322.         line = ''
  323.         for t in tokens:
  324.             if line == '':
  325.                 x = t
  326.             else:
  327.                 x = ' ' + t
  328.             if len(line) + len(x) >= 80:
  329.                 lines.append(line)
  330.                 line = t
  331.                 continue
  332.             line += x
  333.         
  334.         lines.append(line)
  335.         return lines
  336.  
  337.     
  338.     def setTag(self, name, value):
  339.         """Set a PGN tag.
  340.         
  341.         'name' is the name of the tag to set (string).
  342.         'value' is the value to set the tag to (string) or None to delete the tag.
  343.         
  344.         Tag names cannot contain whitespace.
  345.         
  346.         Deleting a tag that does not exist has no effect.
  347.         
  348.         Deleting a STR tag or setting one to an invalid value will raise an Error exception.
  349.         """
  350.         if self._isValidTagName(name) is False:
  351.             raise Error('%s is an invalid tag name' % str(name))
  352.         self._isValidTagName(name) is False
  353.         if value is None:
  354.             if self._strTags.has_key(name):
  355.                 raise Error('%s is a PGN STR tag and cannot be deleted' % name)
  356.             self._strTags.has_key(name)
  357.             
  358.             try:
  359.                 self._strTags.pop(name)
  360.             except KeyError:
  361.                 pass
  362.             except:
  363.                 None<EXCEPTION MATCH>KeyError
  364.             
  365.  
  366.         None<EXCEPTION MATCH>KeyError
  367.         self.tagsByName[name] = value
  368.  
  369.     
  370.     def getTag(self, name, default = None):
  371.         """Get a PGN tag.
  372.         
  373.         'name' is the name of the tag to get (string).
  374.         'default' is the default value to return if this valid is missing (user-defined).
  375.         
  376.         Return the value of the tag (string) or the default if the tag does not exist.
  377.         """
  378.         
  379.         try:
  380.             return self.tagsByName[name]
  381.         except KeyError:
  382.             return default
  383.  
  384.  
  385.     
  386.     def addMove(self, move):
  387.         self.moves.append(move)
  388.  
  389.     
  390.     def getMove(self, moveNumber):
  391.         return self.moves[moveNumber - 1]
  392.  
  393.     
  394.     def getMoves(self):
  395.         return self.moves
  396.  
  397.     
  398.     def __str__(self):
  399.         string = ''
  400.         for tag, value in self.tagsByName.iteritems():
  401.             string += '%s = %s\n' % (tag, value)
  402.         
  403.         string += '\n'
  404.         number = 1
  405.         moves = self.moves
  406.         while len(moves) >= 2:
  407.             string += '%3i. %s %s\n' % (number, moves[0].move, moves[1].move)
  408.             number += 1
  409.             moves = moves[2:]
  410.         if len(moves) > 0:
  411.             string += '%3i. %s\n' % (number, moves[0].move)
  412.         
  413.         return string
  414.  
  415.     
  416.     def _makePGNString(self, string):
  417.         '''Make a PGN string.
  418.         
  419.         \'string\' is the string to convert to a PGN string (string).
  420.         
  421.         All characters are valid and quotes are escaped with \'"\'.
  422.         
  423.         Return the string surrounded with quotes. e.g. \'Mike "Dog" Smith\' -> \'"Mike "Dog" Smith"\'
  424.         '''
  425.         pgnString = string
  426.         pgnString.replace('"', '\\"')
  427.         return '"' + pgnString + '"'
  428.  
  429.     
  430.     def _isValidTagName(self, name):
  431.         """Valid a PGN tag name.
  432.         
  433.         'name' is the tag name to validate (string).
  434.         
  435.         Tags can only contain the characters, a-Z A-Z and _.
  436.         
  437.         Return True if this is a valid tag name otherwise return False.
  438.         """
  439.         if name is None or len(name) == 0:
  440.             return False
  441.         validCharacters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_'
  442.         for c in name:
  443.             if validCharacters.find(c) < 0:
  444.                 return False
  445.         
  446.         return True
  447.  
  448.  
  449.  
  450. class PGN:
  451.     '''
  452.     '''
  453.     __games = None
  454.     
  455.     def __init__(self, fileName = None, maxGames = None):
  456.         """Create a PGN reader/writer.
  457.         
  458.         'fileName' is the file to load the PGN from or None to generate an empty PGN file.
  459.         'maxGames' is the maximum number of games to load from the file or None
  460.                    to load the whole file. (int, Only applicable if a filename is supplied).
  461.         """
  462.         self._PGN__games = []
  463.         if fileName is not None:
  464.             self._PGN__load(fileName, maxGames)
  465.         
  466.  
  467.     
  468.     def addGame(self):
  469.         '''Add a new game to the PGN file.
  470.         
  471.         Returns the PGNGame instance to modify'''
  472.         game = PGNGame()
  473.         self._PGN__games.append(game)
  474.         return game
  475.  
  476.     
  477.     def getGame(self, index):
  478.         """Get a game from the PGN file.
  479.         
  480.         'index' is the game index to get (integer, 0-N).
  481.         
  482.         Return this PGN game or raise an IndexError if no game with this index.
  483.         """
  484.         return self._PGN__games[index]
  485.  
  486.     
  487.     def save(self, fileName):
  488.         """Save the PGN file.
  489.         
  490.         'fileName' is the name of the file to save to.
  491.         """
  492.         f = file(fileName, 'w')
  493.         f.write('; PGN saved game generated by glChess\n')
  494.         f.write('; http://glchess.sourceforge.net\n')
  495.         for game in self._PGN__games:
  496.             f.write('\n')
  497.             for line in game.getLines():
  498.                 f.write(line + '\n')
  499.             
  500.         
  501.         f.close()
  502.  
  503.     
  504.     def __len__(self):
  505.         return len(self._PGN__games)
  506.  
  507.     
  508.     def __getitem__(self, index):
  509.         return self._PGN__games[index]
  510.  
  511.     
  512.     def __getslice__(self, start, end):
  513.         return self._PGN__games[start:end]
  514.  
  515.     
  516.     def __load(self, fileName, maxGames = None):
  517.         '''
  518.         '''
  519.         f = file(fileName, 'r')
  520.         p = PGNParser(maxGames)
  521.         lineNumber = 0
  522.         
  523.         try:
  524.             for line in f.readlines():
  525.                 lineNumber += 1
  526.                 p.parseLine(line)
  527.             
  528.             p.complete()
  529.         except Error:
  530.             e = None
  531.             raise Error('Error on line %d: %s' % (lineNumber, e.message))
  532.  
  533.         self._PGN__games = p.games
  534.         if len(self._PGN__games) == 0:
  535.             raise Error('Empty PGN file')
  536.         len(self._PGN__games) == 0
  537.         f.close()
  538.  
  539.  
  540. if __name__ == '__main__':
  541.     import time
  542.     
  543.     def test(fileName, maxGames = None):
  544.         s = time.time()
  545.         p = PGN(fileName, maxGames)
  546.         print time.time() - s
  547.         number = 1
  548.         games = p[:]
  549.  
  550.  
  551.